/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package battleshipgame.manager;

import battleshipgame.logic.AlreadyBombedException;
import battleshipgame.logic.Battleship;
import battleshipgame.logic.Constants;
import battleshipgame.logic.ExitSignalException;
import battleshipgame.logic.InvalidInputException;
import battleshipgame.logic.Player;
import battleshipgame.ui.WebIOManager;
import battleshipgame.ui.WebIOManager.UserInputType;
import battleshipgame.ui.WebIOManager.SessionState;
import battleshipgame.ui.WebIOManualInputHandler;
import java.util.ArrayList;

/**
 * @shaked
 * main logic class
 */
public class GameManager {
    
    public final static int MIN_NUM_PLAYERS = 2;
    public final static int MAX_NUM_PLAYERS = 6;
    
    private int currentPlayerIndex;
    private int numOfPlayers ;
    private boolean shouldAdvanceToNextPlayer;
    public SessionState currentState;
    public String coordinateSelected;

    //public Player[]  players;
    public ArrayList<Player> players;
    public  Player currentPlayer;
    //public Player currentOpponent;
    public int totalPlayers;
    public UserInputType currentUserInput;
    public WebIOManager currentWebIOManager;


    // used for init game  process
    public String playerNameToAdd;
    public String battleshipNameToAdd;
    public Player.Direction battleshipDirectionToAdd;
    public Player.TypeHumanOrBot currentPlayerType;

    public GameManager()
    {
        init();
    }

    private void init()
    {
                totalPlayers= 0;
        currentPlayerIndex = 0;
        players = new ArrayList<Player>();
        currentState = SessionState.GAME_STARTED;
        currentUserInput = UserInputType.NONE;
        currentPlayer = null;
        //currentOpponent = null;
        coordinateSelected = null;
    }
    
    /*
     * @shaked
     * Main game method. runs until game is finished
     */
    public void playTheGame(WebIOManager webIOManager) throws InvalidInputException
    {
            shouldAdvanceToNextPlayer = false;
            currentWebIOManager = webIOManager;

            if(currentState != SessionState.IN_GAME_WAIT_FOR_USER_INPUT &&
                    currentState != SessionState.WAIT_FOR_ALL_USERS_TO_LOGIN)
            {
                handleInitGame();
            }
            if(currentState == SessionState.WAIT_FOR_ALL_USERS_TO_LOGIN)
            {
                if(!(allUsersConnected()))
                {
                    if(getUserPlayer() == null)
                    {
                            currentWebIOManager.handeUserLogin();
                    }
                    else
                    {
                            waitGame();
                    }
                }
                else
                {
                    currentState = SessionState.IN_GAME_WAIT_FOR_USER_INPUT;
                    //TODO: add init of clock here
                }

            }
            if(currentState == SessionState.IN_GAME_WAIT_FOR_USER_INPUT && getUserPlayer() == null)
            {
                  currentWebIOManager.showErrorPage();
            }
            else if(currentState == SessionState.IN_GAME_WAIT_FOR_USER_INPUT)
            {

                // hanldes the game logic
                handleGame();

                /*
                if(!(gameFinished()) && shouldAdvanceToNextPlayer)
                {
                    advanceToNextPlayer();
                }*/
                
                if(gameFinished())
                {
                    Player winner = getWinner();
                    webIOManager.declareWinner(winner.getName());
                }
                else if(currentPlayer.getHumanOrBot() == Player.TypeHumanOrBot.HUMAN)
                {
                        webIOManager.printGame(currentPlayer, getUserPlayer());
                 }
                else
                {
                    webIOManager.printGame(currentPlayer, getUserPlayer());
                }
        }

    }

    public  boolean allUsersConnected()
    {
        boolean allUsersConnected = true;
        for(int i=0; i< players.size(); i++)
        {
            if (players.get(i).getHumanOrBot() == Player.TypeHumanOrBot.HUMAN && !players.get(i).isConnected())
            {
                allUsersConnected = false;
            }
        }
        return allUsersConnected;
    }
    private void waitGame()
    {
        currentWebIOManager.printWaitGameScreen(getUserPlayer().getName());
    }

    private void handleInitGame() throws InvalidInputException
    {
            if(currentState == SessionState.GAME_STARTED)
            {
                currentWebIOManager.showWelcomeScreen();
                currentState = SessionState.SELECT_INPUT_TYPE;
            }
            // init game board
            else if (currentState == SessionState.SELECT_INPUT_TYPE && currentUserInput == UserInputType.XML_INPUT)
            {
                    XmlHandler.XML_FILE_PATH = currentWebIOManager.currentRequest.getRealPath("/") + "battleship.xml";
                    XmlHandler.getGameDataFromXml(this);

                    // save a backup for Init Process
                    XmlHandler.saveGameDataToXml(this,currentWebIOManager.currentRequest.getRealPath("/") + "backup.xml");

                    currentState = SessionState.WAIT_FOR_ALL_USERS_TO_LOGIN;
             }
             else if(currentUserInput == UserInputType.INIT_GAME_FINISHED)
            {
                // save a backup for Init Process
                XmlHandler.saveGameDataToXml(this,currentWebIOManager.currentRequest.getRealPath("/") + "backup.xml");
                numOfPlayers = players.size();
                for(int i=0; i< players.size(); i++)
                {
                    players.get(i).initMarkBoards(numOfPlayers, players);
                }
                currentState = SessionState.WAIT_FOR_ALL_USERS_TO_LOGIN;
            }
            else if (currentState == SessionState.SELECT_INPUT_TYPE || currentState == SessionState.MANUAL_INPUT_STATE)// MANUAL_INPUT

             {
                    WebIOManualInputHandler.getGameDataFromUser(this);
                    //throw new UnsupportedOperationException("Not yet implemented");
             }

            if(currentUserInput == UserInputType.RESTART_GAME)
            {
                init();
                XmlHandler.getGameDataFromXml(this,
                        currentWebIOManager.currentRequest.getRealPath("/") + "backup.xml");
                currentState = SessionState.WAIT_FOR_ALL_USERS_TO_LOGIN;
            }

    }
private void handleGame() throws InvalidInputException
    {
                currentPlayer = players.get(currentPlayerIndex);
                Player currentOpponent = getUserPlayer().getCurrentOpponent();

                if(currentUserInput == UserInputType.WITHDRAW)
                {
                    getUserPlayer().withdraw();
                    return;
                }

                if(currentUserInput == UserInputType.SELECT_OPPONENT)
                {
                    ;
                    // opponent changed. print his table
                }
                else if(currentUserInput == UserInputType.MARK_SQUARE)
                {
                     if(!(currentPlayer.getMarkBoard(currentOpponent.getName()).IsMarked(coordinateSelected)))
                     {
                       currentPlayer.markPlayer(currentOpponent, coordinateSelected);
                     }
                }
                else if(currentUserInput == UserInputType.CANCEL_MARK_SQUARE)
                {
                     //currentOpponent = null;
                     if((currentPlayer.getMarkBoard(currentOpponent.getName()).IsMarked(coordinateSelected)))
                     {
                       currentPlayer.markPlayer(currentOpponent, coordinateSelected);
                     }
                }
                else if(currentUserInput == UserInputType.BOMB_SQUARE)
                {
                        try
                        {
                             boolean bombedABattleship = currentPlayer.bombPlayer(currentOpponent, coordinateSelected);
                             if (!bombedABattleship)
                             {
                                shouldAdvanceToNextPlayer = true;
                                currentOpponent = null;
                             }
                        }
                        catch(AlreadyBombedException e)
                        {
                            currentWebIOManager.AlertAlreadyBombed();
                        }
                }
                if(currentOpponent != null && currentOpponent.IsPlayerDead())
                {
                    currentOpponent = null;
                }
                
                if(!(gameFinished()) && shouldAdvanceToNextPlayer)
                {
                    advanceToNextPlayer();
                    shouldAdvanceToNextPlayer = false;
                }

}

/*private boolean handleBotPlayer(WebIOManager webIOManager) throws InvalidInputException
    {
    boolean shouldAdvanceToNextPlayer = false;
     // handle  for bot player
    
     boolean bombedABattleship = randomBomb(webIOManager);

     if (!bombedABattleship){
            shouldAdvanceToNextPlayer = true;
     }
    currentUserInput = UserInputType.NONE;
    
        
    return shouldAdvanceToNextPlayer;
    }
 * 
 */
    public void setOpponent(int opponentId)
    {
        if(opponentId != -1)
        {
            //currentOpponent = players.get(opponentId);
            getUserPlayer().setCurrentOpponent(players.get(opponentId));
        }
    }

    public void setCoordinateSelected(String coordinateSelected)
    {
        this.coordinateSelected = coordinateSelected;
    }

    public void setPlayerNameToAdd(String playerNameToAdd){
        this.playerNameToAdd = playerNameToAdd;
    }

    public void setBattleshipNameToAdd(String battleshipNameToAdd){
        this.battleshipNameToAdd = battleshipNameToAdd;
    }

    public void setCurrentPlayer(int playerIndex)
    {
        this.currentPlayerIndex = playerIndex;
        this.currentPlayer = players.get(playerIndex);
    }

    public void setPlayerType(Player.TypeHumanOrBot playerType){
        this.currentPlayerType = playerType;
    }
    public void setBattleshipDirectionToAdd(Player.Direction battleshipDirectionToAdd){
        this.battleshipDirectionToAdd = battleshipDirectionToAdd;
    }

    public void setCurrentUserInput(UserInputType currentUserInput)
    {
        this.currentUserInput = currentUserInput;
    }
    
    /*public void removeDeadPlayers()
    {
             //check which players are dead
            for (int i = 0 ; i< players.size(); i++)
            {
                if(players.get(i).IsPlayerDead())
                {
                    if(i == currentPlayerIndex)
                    {
                        //if current player is dead. advance to next player
                        advanceToNextPlayer();
                    }
                    players.remove(i);
                }
          }
    }
     * /
     */
    
    public int advanceToNextPlayer()
    {
         // advance to next player
         currentPlayerIndex++;
         if (currentPlayerIndex >= players.size())
         {
              currentPlayerIndex = 0;
         }
         if(players.get(currentPlayerIndex).IsPlayerDead())
         {
                 return advanceToNextPlayer();
        }
        else
         {
                  currentPlayer = players.get(currentPlayerIndex);
                 return currentPlayerIndex;
        }
    }
    public void addPlayer(Player player)
    {
        totalPlayers++;
        players.add(player) ;
    }


    public Player getPlayer(int i)
    {
        return this.players.get(i);
    }

    public boolean initGameFinished()
    {
        boolean finished = true;
        for(int i = 0; i<players.size();i++)
        {
            Player player = players.get(i);
            if(player.getTotalBattleship() != Constants.NUM_BATTLESHIPS)
            {
                finished = false;
            }
        }
        if(players.size() < 2 || players.size() > Constants.MAX_NUM_OF_PLAYERS)
        {
            finished = false;
        }
        return finished;
    }

    public boolean gameFinished() {

        /*@shaked TODO add logic to check if game haven't finished yet
         * 
         */
        int countPlayersAlive = 0;
        for(int i = 0; i<players.size() ; i++)
        {
            if(players.get(i).IsPlayerDead() == false)
            {
                countPlayersAlive++;
            }
        }
        return (countPlayersAlive == 1);
    }


    private Player getWinner() {
        /*
         * @shaked TODO add get winner logic here
         */
        //return players.get(0);
        int i;
        for(i = 0; i<players.size() ; i++)
        {
            if(players.get(i).IsPlayerDead() == false)
            {
                break;
            }
        }
        return players.get(i);
    }

    public ArrayList<Player> getPlayers() {
        return players;
    }
    public Player getCurrentPlayer()
    {
        currentPlayer = players.get(currentPlayerIndex);
        return currentPlayer;
    }

    public SessionState getCurrentState()
    {
        return currentState;
    }

    public void setUserPlayer(int userId)
    {
        players.get(userId).connect();
        currentWebIOManager.currentRequest.getSession(true).setAttribute("userPlayer", players.get(userId));
    }

    public Player getUserPlayer()
    {
        if(currentWebIOManager.currentRequest.getSession(true).getAttribute("userPlayer") == null)
            return null;
         else
        {
            return (Player)currentWebIOManager.currentRequest.getSession(true).getAttribute("userPlayer");
        }
    }

    public  boolean randomBomb()
    {
        int randomPlayerIndex = 0;
            do
            {
             randomPlayerIndex = (int)(Math.random()*players.size());
            }
            while(getCurrentPlayer().getId() ==randomPlayerIndex ||
                    players.get((randomPlayerIndex)).IsPlayerDead());
        do
        {
            int randomXAxis = (int)(Math.random()*Constants.BOARD_SIZE);
            int randomYAxis = (int)(Math.random()*Constants.BOARD_SIZE + 1);
            String coordiante;
            randomXAxis += 'A';
            if(randomYAxis !=10)
            {
                randomYAxis += '0';
                coordiante = (char)randomXAxis + ""  + (char)randomYAxis;
            }
            else
            {
                coordiante = (char)randomXAxis + "10";
            }
            try
            {
                return getCurrentPlayer().bombPlayer(players.get(randomPlayerIndex), coordiante);
            }
            catch(AlreadyBombedException e)
            {
            }
            catch(InvalidInputException e)
            {
                 // should never get here
                int x=-1;
            }
        }
        while(true);
    }

}
